#include "TcpClient.h"
#include <Arduino.h>

// 构造函数初始化成员变量
TcpClient::TcpClient(const char* host, uint16_t port)
  : _serverHost(host),
    _serverPort(port),
    _isConnected(false),
    _reconnectInterval(5000),  // 默认重连间隔5秒
    _lastReconnectAttempt(0) {}

// 初始化客户端（可在此处进行额外初始化操作）
void TcpClient::begin() {
  // 初始连接尝试
  if (!_isConnected) {
    connectToServer();
  }
}

// 主循环处理函数（需在主程序loop中调用）
void TcpClient::loop() {
  // 处理连接状态
  if (!_isConnected) {
    unsigned long now = millis();

    // 检查是否达到重连时间间隔
    if (now - _lastReconnectAttempt > _reconnectInterval) {
      _lastReconnectAttempt = now;
      if (connectToServer()) {
        _lastReconnectAttempt = 0;  // 连接成功重置计时器
      }
    }

  } else {
    // 连接状态下处理接收数据
    handleReceivedData();
    // 检查连接是否断开
    if (!_client.connected()) {
      _isConnected = false;
      if (_onConnectionStatusChanged) {
        _onConnectionStatusChanged(false);
      }
    }
  }
}

// 发送字符串类型数据
bool TcpClient::send(uint16_t msgType, const String& data) {
  return send(msgType, (const uint8_t*)data.c_str(), data.length());
}

// 发送字节数组类型数据
bool TcpClient::send(uint16_t msgType, const uint8_t* data, size_t length) {
  if (!_isConnected) return false;

  // 协议格式：[消息类型(2字节)][数据长度(4字节)][数据内容]
  // 注意：实际应用中建议添加字节序转换（htons/htonl）
  uint16_t netMsgType = msgType;  // 如需跨平台可使用htons(msgType)
  uint32_t netDataLen = length;   // 如需跨平台可使用htonl(length)

  _client.write((const uint8_t*)&netMsgType, sizeof(netMsgType));
  _client.write((const uint8_t*)&netDataLen, sizeof(netDataLen));
  _client.write(data, length);

  _client.flush();  // 等待数据发送完成
  return true;
}

// 获取当前连接状态
bool TcpClient::isConnected() {
  return _isConnected;
}

// 设置消息接收回调
void TcpClient::setOnMessageReceived(std::function<void(const String&, uint16_t)> callback) {
  _onMessageReceived = callback;
}

// 设置连接状态变更回调
void TcpClient::setOnConnectionStatusChanged(std::function<void(bool)> callback) {
  _onConnectionStatusChanged = callback;
}

// 主动断开连接
void TcpClient::disconnect() {
  if (_isConnected) {
    _client.stop();
    _isConnected = false;
    if (_onConnectionStatusChanged) {
      _onConnectionStatusChanged(false);
    }
  }
}

// 内部连接服务器实现
bool TcpClient::connectToServer() {
  if (_client.connect(_serverHost, _serverPort)) {
    _isConnected = true;
    if (_onConnectionStatusChanged) {
      _onConnectionStatusChanged(true);
    }
    return true;
  } else {
    _isConnected = false;
    if (_onConnectionStatusChanged) {
      _onConnectionStatusChanged(false);
    }
    return false;
  }
}

// 内部处理接收数据（修正后）
void TcpClient::handleReceivedData() {
  // 协议格式：2字节类型 + 4字节长度 + 数据区
  // 先检查是否有足够的头部数据（2+4=6字节）
  while (_client.available() >= 6) {
    // 1. 读取2字节消息类型
    uint16_t msgType;
    _client.read((uint8_t*)&msgType, sizeof(msgType));
    // 如需跨平台，建议添加字节序转换：msgType = ntohs(msgType);

    // 2. 读取4字节数据长度
    uint32_t dataLen;
    _client.read((uint8_t*)&dataLen, sizeof(dataLen));
    // 如需跨平台，建议添加字节序转换：dataLen = ntohl(dataLen);

    // 检查数据长度是否合理（防止内存分配过大）
    if (dataLen == 0 || dataLen > 1024 * 10) {  // 假设最大10KB
      Serial.printf("无效的数据长度: %u\n", dataLen);
      // 跳过无效数据（谨慎处理，这里简单跳过当前头部）
      continue;
    }

    // 3. 等待数据区完整接收
    if (_client.available() >= dataLen) {
      // 分配缓冲区存储数据
      char* buffer = new char[dataLen + 1];  // +1用于存储字符串结束符
      _client.read((uint8_t*)buffer, dataLen);
      buffer[dataLen] = '\0';  // 添加字符串结束符

      // 触发回调，可根据需要包含消息类型（这里示例仅传递数据）
      if (_onMessageReceived) {
        // 如需传递消息类型，可修改回调参数为包含类型的结构体或组合字符串
        _onMessageReceived(String(buffer), msgType);
      }

      // 释放缓冲区
      delete[] buffer;
    } else {
      // 数据不完整，等待后续数据（退出循环，下次loop再处理）
      break;
    }
  }
}